#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Configuration Management Module
Handles loading, saving and validating system configurations
"""

import logging
import json
import os
import copy
from typing import Dict, List, Any, Optional
from datetime import datetime

logger = logging.getLogger("config_manager")

class ConfigManager:
    """Configuration Management Class"""
    
    def __init__(self, config_dir: str = "./config"):
        """Initialize configuration manager
        
        Args:
            config_dir: Directory to store configuration files
        """
        self.config_dir = config_dir
        self.config_files = {
            'system': 'system_config.json',
            'cameras': 'cameras_config.json',
            'detection': 'detection_config.json',
            'alerts': 'alerts_config.json'
        }
        
        self.config = {
            'system': self._get_default_system_config(),
            'cameras': {},
            'detection': self._get_default_detection_config(),
            'alerts': self._get_default_alerts_config()
        }
        
        # Ensure config directory exists
        if not os.path.exists(self.config_dir):
            try:
                os.makedirs(self.config_dir)
                logger.info(f"Created config directory: {self.config_dir}")
            except Exception as e:
                logger.error(f"Failed to create config directory: {str(e)}")
        
        logger.info("Configuration manager initialized")
    
    def _get_default_system_config(self) -> Dict[str, Any]:
        """Get default system configuration"""
        return {
            'general': {
                'app_name': 'YOLO Apply',
                'version': '1.0.0',
                'log_level': 'INFO',
                'max_workers': 4,
                'auto_start_processing': True
            },
            'processing': {
                'frame_rate': 15,
                'max_retries': 3,
                'retry_delay_seconds': 1.0,
                'error_threshold': 10,
                'error_cooldown_seconds': 30
            },
            'storage': {
                'database_path': './yolo_apply.db',
                'max_detection_history_days': 30,
                'max_alert_history_days': 90,
                'backup_enabled': True,
                'backup_interval_hours': 24
            },
            'monitoring': {
                'enabled': True,
                'interval_seconds': 1.0,
                'max_history_seconds': 3600
            }
        }
    
    def _get_default_detection_config(self) -> Dict[str, Any]:
        """Get default detection configuration"""
        return {
            'model': {
                'type': 'yolo11',
                'weights_path': './models/yolo11n.pt',
                'confidence_threshold': 0.5,
                'iou_threshold': 0.45,
                'max_detections': 100
            },
            'tracking': {
                'enabled': True,
                'track_buffer': 30,
                'match_thresh': 0.8,
                'min_box_area': 100,
                'mot20': False
            },
            'device': {
                'type': 'auto',  # auto, cpu, cuda, mps
                'device_id': 0
            },
            'classes': {
                'person': True,
                'vehicle': True,
                'traffic_analysis': True,
                'face': True
            },
            'optimization': {
                'half_precision': True,
                'fuse_layers': True,
                'enable_cuda_graph': False
            }
        }
    
    def _get_default_alerts_config(self) -> Dict[str, Any]:
        """Get default alerts configuration"""
        return {
            'general': {
                'enabled': True,
                'dispatch_cooldown_seconds': 30,
                'max_alerts_per_minute': 60
            },
            'channels': {
                'log': True,
                'email': False,
                'sms': False,
                'webhook': False
            },
            'email': {
                'smtp_server': '',
                'smtp_port': 587,
                'use_ssl': False,
                'username': '',
                'password': '',
                'from_email': '',
                'recipients': []
            },
            'sms': {
                'provider': '',
                'api_key': '',
                'phone_numbers': []
            },
            'webhook': {
                'url': '',
                'headers': {},
                'timeout_seconds': 5
            },
            'priorities': {
                'high': {
                    'retry_count': 3,
                    'retry_interval_seconds': 60
                },
                'medium': {
                    'retry_count': 2,
                    'retry_interval_seconds': 120
                },
                'low': {
                    'retry_count': 1,
                    'retry_interval_seconds': 300
                }
            }
        }
    
    def _get_default_camera_config(self) -> Dict[str, Any]:
        """Get default camera configuration"""
        return {
            'enabled': True,
            'source': '',
            'name': '',
            'location': '',
            'description': '',
            'resolution': {
                'width': 1280,
                'height': 720
            },
            'processing': {
                'enabled': True,
                'detection_enabled': True,
                'tracking_enabled': True,
                'behavior_analysis_enabled': True
            },
            'roi': {
                'enabled': False,
                'polygon': []
            },
            'restricted_areas': [],
            'loitering_detection': {
                'enabled': False,
                'threshold_seconds': 30
            },
            'alerts': {
                'enabled': True,
                'alert_on': ['all']
            }
        }
    
    def load_all_configs(self) -> bool:
        """Load all configuration files
        
        Returns:
            True if successful, False otherwise
        """
        try:
            success = True
            
            # Load each config type
            for config_type, filename in self.config_files.items():
                if not self._load_config(config_type, filename):
                    success = False
            
            if success:
                logger.info("All configurations loaded successfully")
            else:
                logger.warning("Some configurations failed to load")
            
            return success
            
        except Exception as e:
            logger.error(f"Error loading all configs: {str(e)}")
            return False
    
    def _load_config(self, config_type: str, filename: str) -> bool:
        """Load specific configuration file
        
        Args:
            config_type: Configuration type
            filename: Configuration filename
            
        Returns:
            True if successful, False otherwise
        """
        try:
            file_path = os.path.join(self.config_dir, filename)
            
            if os.path.exists(file_path):
                with open(file_path, 'r', encoding='utf-8') as f:
                    loaded_config = json.load(f)
                    
                    # Validate and merge with defaults if needed
                    if config_type == 'system':
                        self.config['system'] = self._merge_configs(
                            self._get_default_system_config(), 
                            loaded_config
                        )
                    elif config_type == 'detection':
                        self.config['detection'] = self._merge_configs(
                            self._get_default_detection_config(), 
                            loaded_config
                        )
                    elif config_type == 'alerts':
                        self.config['alerts'] = self._merge_configs(
                            self._get_default_alerts_config(), 
                            loaded_config
                        )
                    elif config_type == 'cameras':
                        # For cameras, validate each camera config
                        self.config['cameras'] = {}
                        for camera_id, camera_config in loaded_config.items():
                            self.config['cameras'][camera_id] = self._merge_configs(
                                self._get_default_camera_config(),
                                camera_config
                            )
                    
                    logger.info(f"Loaded {config_type} configuration from {file_path}")
                    return True
            else:
                # File doesn't exist, use defaults and save
                logger.info(f"Configuration file {file_path} not found, using defaults")
                self.save_config(config_type)
                return True
                
        except Exception as e:
            logger.error(f"Error loading {config_type} config: {str(e)}")
            return False
    
    def save_all_configs(self) -> bool:
        """Save all configuration files
        
        Returns:
            True if successful, False otherwise
        """
        try:
            success = True
            
            # Save each config type
            for config_type in self.config_files:
                if not self.save_config(config_type):
                    success = False
            
            if success:
                logger.info("All configurations saved successfully")
            else:
                logger.warning("Some configurations failed to save")
            
            return success
            
        except Exception as e:
            logger.error(f"Error saving all configs: {str(e)}")
            return False
    
    def save_config(self, config_type: str) -> bool:
        """Save specific configuration
        
        Args:
            config_type: Configuration type
            
        Returns:
            True if successful, False otherwise
        """
        try:
            if config_type not in self.config_files:
                logger.error(f"Unknown config type: {config_type}")
                return False
            
            filename = self.config_files[config_type]
            file_path = os.path.join(self.config_dir, filename)
            
            # Add metadata
            config_to_save = copy.deepcopy(self.config.get(config_type, {}))
            config_to_save['_metadata'] = {
                'last_modified': datetime.now().isoformat(),
                'version': '1.0'
            }
            
            # Create directory if it doesn't exist
            os.makedirs(self.config_dir, exist_ok=True)
            
            # Write to file with pretty formatting
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(config_to_save, f, indent=2, ensure_ascii=False)
            
            logger.info(f"Saved {config_type} configuration to {file_path}")
            return True
            
        except Exception as e:
            logger.error(f"Error saving {config_type} config: {str(e)}")
            return False
    
    def get_config(self, config_type: str) -> Dict[str, Any]:
        """Get configuration by type
        
        Args:
            config_type: Configuration type
            
        Returns:
            Configuration dictionary
        """
        if config_type in self.config:
            # Return a copy to prevent accidental modification
            return copy.deepcopy(self.config[config_type])
        else:
            logger.warning(f"Unknown config type requested: {config_type}")
            return {}
    
    def set_config(self, config_type: str, config: Dict[str, Any]) -> bool:
        """Set configuration by type
        
        Args:
            config_type: Configuration type
            config: New configuration
            
        Returns:
            True if successful, False otherwise
        """
        try:
            if config_type not in self.config:
                logger.error(f"Unknown config type: {config_type}")
                return False
            
            # Validate config
            if not self._validate_config(config_type, config):
                logger.error(f"Invalid configuration for {config_type}")
                return False
            
            # Remove metadata if present
            if '_metadata' in config:
                del config['_metadata']
            
            # Merge with existing config
            if config_type == 'system':
                self.config['system'] = self._merge_configs(
                    self.config['system'], 
                    config
                )
            elif config_type == 'detection':
                self.config['detection'] = self._merge_configs(
                    self.config['detection'], 
                    config
                )
            elif config_type == 'alerts':
                self.config['alerts'] = self._merge_configs(
                    self.config['alerts'], 
                    config
                )
            elif config_type == 'cameras':
                # For cameras, merge each camera config
                for camera_id, camera_config in config.items():
                    if camera_id in self.config['cameras']:
                        self.config['cameras'][camera_id] = self._merge_configs(
                            self.config['cameras'][camera_id],
                            camera_config
                        )
                    else:
                        self.config['cameras'][camera_id] = self._merge_configs(
                            self._get_default_camera_config(),
                            camera_config
                        )
            
            logger.info(f"Set {config_type} configuration")
            return True
            
        except Exception as e:
            logger.error(f"Error setting {config_type} config: {str(e)}")
            return False
    
    def add_camera_config(self, camera_id: str, camera_config: Dict[str, Any]) -> bool:
        """Add new camera configuration
        
        Args:
            camera_id: Camera identifier
            camera_config: Camera configuration
            
        Returns:
            True if successful, False otherwise
        """
        try:
            # Merge with default config
            merged_config = self._merge_configs(
                self._get_default_camera_config(),
                camera_config
            )
            
            # Set camera ID in the config
            merged_config['camera_id'] = camera_id
            
            # Add to cameras config
            self.config['cameras'][camera_id] = merged_config
            
            logger.info(f"Added camera configuration for {camera_id}")
            return True
            
        except Exception as e:
            logger.error(f"Error adding camera config for {camera_id}: {str(e)}")
            return False
    
    def get_camera_config(self, camera_id: str) -> Optional[Dict[str, Any]]:
        """Get specific camera configuration
        
        Args:
            camera_id: Camera identifier
            
        Returns:
            Camera configuration or None if not found
        """
        cameras = self.get_config('cameras')
        if camera_id in cameras:
            return copy.deepcopy(cameras[camera_id])
        else:
            logger.warning(f"Camera config not found for {camera_id}")
            return None
    
    def remove_camera_config(self, camera_id: str) -> bool:
        """Remove camera configuration
        
        Args:
            camera_id: Camera identifier
            
        Returns:
            True if successful, False otherwise
        """
        try:
            if camera_id in self.config['cameras']:
                del self.config['cameras'][camera_id]
                logger.info(f"Removed camera configuration for {camera_id}")
                return True
            else:
                logger.warning(f"Camera config not found for {camera_id}")
                return False
                
        except Exception as e:
            logger.error(f"Error removing camera config for {camera_id}: {str(e)}")
            return False
    
    def _merge_configs(self, default: Dict[str, Any], new: Dict[str, Any]) -> Dict[str, Any]:
        """Recursively merge two configuration dictionaries
        
        Args:
            default: Default configuration
            new: New configuration to merge
            
        Returns:
            Merged configuration
        """
        merged = copy.deepcopy(default)
        
        for key, value in new.items():
            # Skip metadata
            if key == '_metadata':
                continue
                
            if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
                # Recursively merge dictionaries
                merged[key] = self._merge_configs(merged[key], value)
            else:
                # Override or add new values
                merged[key] = value
        
        return merged
    
    def _validate_config(self, config_type: str, config: Dict[str, Any]) -> bool:
        """Validate configuration
        
        Args:
            config_type: Configuration type
            config: Configuration to validate
            
        Returns:
            True if valid, False otherwise
        """
        try:
            # Basic validation based on config type
            if config_type == 'system':
                # Check required sections
                required_sections = ['general', 'processing', 'storage', 'monitoring']
                for section in required_sections:
                    if section not in config:
                        logger.error(f"Missing required section in system config: {section}")
                        return False
                
                # Validate log level
                valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
                log_level = config.get('general', {}).get('log_level', 'INFO').upper()
                if log_level not in valid_log_levels:
                    logger.error(f"Invalid log level: {log_level}")
                    return False
                    
            elif config_type == 'detection':
                # Check required sections
                if 'model' not in config:
                    logger.error("Missing model section in detection config")
                    return False
                
                # Validate thresholds
                if config['model'].get('confidence_threshold', 0) < 0 or config['model'].get('confidence_threshold', 1) > 1:
                    logger.error("Confidence threshold must be between 0 and 1")
                    return False
                    
            elif config_type == 'cameras':
                # Validate each camera config
                for camera_id, camera_config in config.items():
                    if 'source' not in camera_config or not camera_config['source']:
                        logger.error(f"Camera {camera_id} missing source")
                        return False
                        
            return True
            
        except Exception as e:
            logger.error(f"Error validating {config_type} config: {str(e)}")
            return False
    
    def export_config(self, config_type: str) -> str:
        """Export configuration as JSON string
        
        Args:
            config_type: Configuration type
            
        Returns:
            JSON string or empty string on error
        """
        try:
            config = self.get_config(config_type)
            return json.dumps(config, indent=2, ensure_ascii=False)
            
        except Exception as e:
            logger.error(f"Error exporting {config_type} config: {str(e)}")
            return ''
    
    def import_config(self, config_type: str, json_str: str) -> bool:
        """Import configuration from JSON string
        
        Args:
            config_type: Configuration type
            json_str: JSON string
            
        Returns:
            True if successful, False otherwise
        """
        try:
            config = json.loads(json_str)
            return self.set_config(config_type, config)
            
        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON for {config_type} config: {str(e)}")
            return False
        except Exception as e:
            logger.error(f"Error importing {config_type} config: {str(e)}")
            return False

# Create singleton instance for easy import
def get_config_manager(config_dir: str = "./config") -> ConfigManager:
    """Get config manager instance
    
    Args:
        config_dir: Directory to store configuration files
        
    Returns:
        ConfigManager instance
    """
    if not hasattr(get_config_manager, '_instance'):
        get_config_manager._instance = ConfigManager(config_dir)
    return get_config_manager._instance